/*
This file is part of CanFestival, a library implementing CanOpen Stack.

Copyright (C): Edouard TISSERANT and Francis DUPIN
AVR Port: Andreas GLAUSER and Peter CHRISTEN

See COPYING file for copyrights details.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/******************************************************************************
Project description:
Test projekt for a DS 401 slave, running on Atmel's STK500 with AT90CAN128
Short description:
  PORTA:    Inputs (Keys, low active)
  PORTB:    Outputs (LEDs, low active)
  PORTC:    Node ID (1 BCD switch, low active)

******************************************************************************/
#include "hardware.h"
#include "canfestival.h"
#include "can_AVR.h"
#include "ObjDict.h"
#include "ds401.h"

unsigned char timer_interrupt = 0;      // Set if timer interrupt eclapsed
unsigned char inputs;

// CAN
unsigned char nodeID;
unsigned char digital_input[1] = {0};
unsigned char digital_output[1] = {0};

static Message m = Message_Initializer;     // contain a CAN message

void sys_init();

// macros to handle the schedule timer
#define sys_timer           timer_interrupt
#define reset_sys_timer()       timer_interrupt = 0
#define CYCLE_TIME              1000        // Sample Timebase [us]

int main(void)
{
    sys_init();                                   // Initialize system
    canInit(CAN_BAUDRATE);                // Initialize the CANopen bus
    initTimer();                                  // Start timer for the CANopen stack
    nodeID = read_bcd();              // Read node ID first
    setNodeId(&ObjDict_Data, nodeID);
    setState(&ObjDict_Data, Initialisation);  // Init the state
    for (;;)                              // forever loop
    {
        if (sys_timer)                          // Cycle timer, invoke action on every time slice
        {
            reset_sys_timer();                    // Reset timer
            digital_input[0] = get_inputs();
            digital_input_handler(&ObjDict_Data, digital_input, sizeof(digital_input));
            digital_output_handler(&ObjDict_Data, digital_output, sizeof(digital_output));
            set_outputs(digital_output[0]);
            // Check if CAN address has been changed
            if (!(nodeID == read_bcd()))
            {
                nodeID = read_bcd();                    // Save the new CAN adress
                setState(&ObjDict_Data, Stopped);         // Stop the node, to change the node ID
                setNodeId(&ObjDict_Data, nodeID);         // Now the CAN adress is changed
                setState(&ObjDict_Data, Pre_operational); // Set to Pre_operational, master must boot it again
            }
        }
        // a message was received pass it to the CANstack
        if (canReceive(&m))         // a message reveived
        {
            canDispatch(&ObjDict_Data, &m);    // process it
        }
        else
        {
            // Enter sleep mode
#ifdef WD_SLEEP       // Watchdog and Sleep
            wdt_reset();
            sleep_enable();
            sleep_cpu();
#endif                // Watchdog and Sleep
        }
    }
}

void sys_init()
/******************************************************************************
Initialize the relays, the main states and the modbus protocol stack.
INPUT   LOCK_STATES *lock_states
OUTPUT  void
******************************************************************************/
{
    OSCCAL = 0x43;                        // adjust the RC oszillator
    PORTA = 0xFF;                         // Inputs (Keys, low active) with pullup
    DDRA  = 0x00;                     //
    PORTB = 0xFF;                         // Outputs (LEDs, low active) all 1
    DDRB  = 0xFF;                     //
    PORTC = 0xFF;                         // 1 BCD switch with pullup
    DDRC  = 0x00;                     //
    PORTD = 0x2C;                         // 2xCOM, unused, CAN, unused
    DDRD  = 0x2A;                     // All init 0 or without pullup
    PORTE = 0x00;                         // Output
    DDRE  = 0x3C;                     // 2x not used, 2x not used
    PORTF = 0x00;                         // Not used
    DDRF  = 0xFF;                     // All output
    PORTG = 0x00;                         // Not used
    DDRG  = 0x1F;                     // Output for debug (only 5 pins)
    // Set timer 0 for main schedule time
    TCCR0A |= 1 << WGM01 | 1 << CS01 | 1 << CS00;// Timer 0 CTC , Timer 0 mit CK/64 starten
    TIMSK0 = 1 << OCIE0A;             // Timer Interrupts: Timer 0 Compare
    OCR0A = (unsigned char)(F_CPU / 64 * CYCLE_TIME / 1000000 - 1); // Reloadvalue for timer 0
#ifdef WD_SLEEP       // Watchdog and Sleep
    wdt_reset();
    wdt_enable(WDTO_15MS);    // Watchdogtimer start with 16 ms timeout
#endif            // Watchdog and Sleep
    sei();         // Enable Interrupts
}


#ifdef  __IAR_SYSTEMS_ICC__
    #pragma type_attribute = __interrupt
    #pragma vector=TIMER0_COMP_vect
    void TIMER0_COMP_interrupt(void)
#else   // GCC
    ISR(TIMER0_COMP_vect)
#endif  // GCC
/******************************************************************************
Interruptserviceroutine Timer 2 Compare A for the main cycle
******************************************************************************/

{
    timer_interrupt = 1;  // Set flag
}
